package com.yu.mall.util;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.yu.mall.vo.ResultMessage;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;

@Slf4j
public class WechatPayUtil {

    //商户号
    private static String merchantId = "1604861287";
    //api证书序列号
    private static String merchantSerialNumber = "48C73B80901B85C0A36ACE1455497E18F8483AC7";
    //api证书本地路径
    private static String merchantPrivateKeyFile = "cert/apiclient_key.pem";
    //api密钥
    private static String apiV3Key = "1uvQ21NH1I5yBLnAX2yUGtxgpp0w6hPe";
    //小程序appid
    private static String appid = "wx53abe3afd4a40a8b";
    private static HttpClient wxHttpClient = null;
    private static PrivateKey merchantPrivateKey = null;
    private static String preUrl;
    private static String projectdPath;

    public static void init(){
        AutoUpdateCertificatesVerifier verifier = null;
        try {
            merchantPrivateKey = getPrivateKey(projectdPath + merchantPrivateKeyFile);
            WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(merchantId,
                    new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey));
            verifier = new AutoUpdateCertificatesVerifier(wechatPay2Credentials,apiV3Key.getBytes("utf-8"));
        } catch (IOException e) {
            log.error("获取商户私钥失败", e);
        }
        wxHttpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                .withWechatpay(Arrays.asList(verifier.getValidCertificate())).build();
    }

    public static void setNotifyUrl(String url){
        preUrl = url;
    }

    public static void setProjectdPath(String path){
        if(path.contains("dev")){
            projectdPath = WechatPayUtil.class.getResource("/").getPath().substring(1);
        }else{
            projectdPath = path;
        }
    }

    public static PrivateKey getPriKey() throws IOException {
        if(null == merchantPrivateKey){
            String projectdPath = WechatPayUtil.class.getResource("/").getPath().substring(1);
            merchantPrivateKey = getPrivateKey(projectdPath + merchantPrivateKeyFile);
        }
        return merchantPrivateKey;
    }

    public static void main(String[] args) {
        try {
            /*HttpGet httpGet = getHttpGet("https://api.mch.weixin.qq.com/v3/certificates");
            HttpResponse response = wxHttpClient.execute(httpGet);
            String respStr = EntityUtils.toString(response.getEntity(), "utf-8");
            System.out.println(respStr);*/

            createOrder(System.currentTimeMillis() + "", 1, "oTC2A5blFaDGALKpQjILnq8aPZsE", "测试");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //生成订单
    public static ResultMessage<JSONObject> createOrder(String outTradeNo, int amount, String openid, String description) {
        JSONObject params = new JSONObject();
        params.put("appid", appid);
        params.put("mchid", merchantId);
        params.put("description", description);
        params.put("out_trade_no", outTradeNo);
        params.put("notify_url", preUrl+"/wx/payNotify");
        JSONObject amountJson = new JSONObject();
        amountJson.put("total", amount);
        amountJson.put("currency", "CNY");
        params.put("amount", amountJson);
        JSONObject payerJson = new JSONObject();
        payerJson.put("openid", openid);
        params.put("payer", payerJson);
        try {
            HttpPost httpPost = getHttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi", params);
            HttpResponse response = wxHttpClient.execute(httpPost);
            int respCode = response.getStatusLine().getStatusCode();
            String respStr = EntityUtils.toString(response.getEntity(), "utf-8");
            log.info("微信下单请求,结果原文,respCode:{},respStr:{}",respCode, respStr);
            if(200 == respCode){
                JSONObject data = JSON.parseObject(respStr);
                String prepay_id = data.getString("prepay_id");
                String timeStamp = System.currentTimeMillis()/1000+"";
                String nonceStr = timeStamp;
                String sign = sign(buildMessage(timeStamp, nonceStr, prepay_id).getBytes("utf-8"));
                data.put("timeStamp",timeStamp);
                data.put("nonceStr",nonceStr);
                data.put("sign",sign);
                return ResultMessage.success("ok",data);
            }
        } catch (Exception e) {
            log.error("微信下单请求，报错", e);
        }
        return ResultMessage.fail("fail");
    }

    //查询订单
    public static ResultMessage<JSONObject> queryOrder(String outTradeNo) {
        try {
            HttpGet httpGet = getHttpGet("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + outTradeNo+"?mchid="+merchantId);
            log.info("queryOrder微信查询支付中url："+httpGet.getURI().toString());
            HttpResponse response = wxHttpClient.execute(httpGet);
            int respCode = response.getStatusLine().getStatusCode();
            String respStr = EntityUtils.toString(response.getEntity(), "utf-8");
            log.info("微信订单查询请求,结果原文,respCode:{},respStr:{}",respCode, respStr);
            if(200 == respCode){
                return ResultMessage.success("ok",JSON.parseObject(respStr));
            }
        } catch (Exception e) {
            log.error("微信订单查询请求，报错", e);
        }
        return ResultMessage.fail("fail");
    }

    //订单退款
    public static ResultMessage<JSONObject> refundOrder(String outTradeNo, String outRefundNo, int orgAmount, int refundAmount, String reason) {
        JSONObject params = new JSONObject();
        params.put("out_trade_no", outTradeNo);
        params.put("out_refund_no", outRefundNo);
        params.put("notify_url", preUrl+"/wx/refundNotify");
        if (StrUtil.isNotBlank(reason)) {
            params.put("reason", reason);
        }
        JSONObject amountJson = new JSONObject();
        amountJson.put("total", orgAmount);
        amountJson.put("refund", refundAmount);
        amountJson.put("currency", "CNY");
        params.put("amount", amountJson);
        try {
            HttpPost httpPost = getHttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds", params);
            HttpResponse response = wxHttpClient.execute(httpPost);
            int respCode = response.getStatusLine().getStatusCode();
            String respStr = EntityUtils.toString(response.getEntity(), "utf-8");
            log.info("微信订单退款请求,结果原文,respCode:{},respStr:{}",respCode, respStr);
            if(200 == respCode){
                return ResultMessage.success("ok",JSON.parseObject(respStr));
            }
        } catch (Exception e) {
            log.error("微信订单退款请求，报错", e);
        }
        return ResultMessage.fail("fail");
    }

    //查询退款订单
    public static ResultMessage<JSONObject> queryRefundOrder(String outRefundNo) {
        try {
            HttpGet httpGet = getHttpGet("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/" + outRefundNo);
            HttpResponse response = wxHttpClient.execute(httpGet);
            int respCode = response.getStatusLine().getStatusCode();
            String respStr = EntityUtils.toString(response.getEntity(), "utf-8");
            log.info("微信退款订单查询请求,结果原文,respCode:{},respStr:{}",respCode, respStr);
            if(200 == respCode){
                return ResultMessage.success("ok",JSON.parseObject(respStr));
            }
        } catch (Exception e) {
            log.error("微信退款订单查询请求，报错", e);
        }
        return ResultMessage.fail("fail");
    }

    private static HttpGet getHttpGet(String url) {
        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader("Accept", "application/json");
        return httpGet;
    }

    private static HttpPost getHttpPost(String url, JSONObject params) {
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Accept", "application/json");
        if (null != params) {
            StringEntity requestEntity = new StringEntity(params.toJSONString(), "utf-8");
            requestEntity.setContentEncoding("UTF-8");
            requestEntity.setContentType("application/json");
            httpPost.setEntity(requestEntity);
        }
        return httpPost;
    }

    /**
     * 获取私钥。
     *
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    private static PrivateKey getPrivateKey(String filename) throws IOException {

        String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

    public static String getReqData(HttpServletRequest request) {
        BufferedReader reader = null;
        StringBuilder sb = new StringBuilder();
        String line = null;
        try {
            if (request.getAttribute("_getReqData") != null) {
                return (String) request.getAttribute("_getReqData");
            }
            reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        request.setAttribute("_getReqData", sb.toString());
        return (String) request.getAttribute("_getReqData");
    }

    public static String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
            throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes("UTF-8"), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce);
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }

    public static String buildMessage(String timestamp, String nonceStr, String prepay_id) {
        return appid + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + "prepay_id="+prepay_id + "\n";
    }

    private static String sign(byte[] message) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(merchantPrivateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }
}
